import type {
  ClickOutsideOptions,
  ClickOutsideResult,
} from './types'
import { logger } from '../../types/constants'
import { DEFAULT_CLICK_OUTSIDE_OPTIONS } from './types'

/**
 * 点击外部关闭处理器
 */
export class ClickOutsideHandler implements ClickOutsideHandler {
  private readonly name: string = 'ClickOutsideHandler'
  private enabled: boolean = false
  private handler: ((event: MouseEvent) => void) | null = null
  private readonly options: Required<ClickOutsideOptions>

  constructor(options: ClickOutsideOptions) {
    this.options = {
      ...DEFAULT_CLICK_OUTSIDE_OPTIONS,
      ...options,
    }

    if (this.options.enabled)
      this.enable()
  }

  /**
   * 启用点击外部监听
   */
  enable(): void {
    const { id, useCapture, excludeSelectors } = this.options

    if (this.isEnabled())
      return

    this.handler = this.createEventHandler()
    document.addEventListener('click', this.handler, useCapture)
    this.enabled = true

    logger.attention(`[${this.name}] ${id} 点击外部监听已启用`, excludeSelectors)
  }

  /**
   * 禁用点击外部监听
   */
  disable(): void {
    const { id, useCapture } = this.options

    if (!this.isEnabled || !this.handler)
      return

    document.removeEventListener('click', this.handler, useCapture)
    this.handler = null
    this.enabled = false

    logger.debug(`[${this.name}] ${id} 点击外部监听已禁用`)
  }

  /**
   * 销毁处理器
   */
  destroy(): void {
    this.disable()
    logger.info(`[${this.name}] ${this.options.id} 点击外部监听已销毁`)
  }

  /**
   * 检查是否已启用
   */
  isEnabled(): boolean {
    return this.enabled
  }

  /**
   * 创建事件处理函数
   */
  private createEventHandler(): (event: MouseEvent) => void {
    return (event: MouseEvent) => {
      if (!this.isEnabled)
        return

      const target = event.target as Element

      // 检查自定义条件
      if (!this.options.condition(event, target))
        return

      // 检查是否在排除的元素内
      if (this.isInExcludedElements(target))
        return

      // 触发外部点击回调
      this.options.onOutsideClick(event)
    }
  }

  /**
   * 检查目标是否在排除的元素内
   */
  private isInExcludedElements(target: Element): boolean {
    return this.options.excludeSelectors.some((selector) => {
      const elements = document.querySelectorAll(selector)
      return Array.from(elements).some(element => element.contains(target))
    })
  }

  /**
   * 创建点击外部处理器
   */
  static createClickOutside(options: ClickOutsideOptions): ClickOutsideResult {
    const handler = new ClickOutsideHandler(options)

    return {
      handler,
      enable: () => handler.enable(),
      disable: () => handler.disable(),
      destroy: () => handler.destroy(),
    }
  }
}
